package com.kmxd.ams.app.arrange.command;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.kmxd.ams.client.archive.IArchiveFieldSvc;
import com.kmxd.ams.client.archive.dto.ArchiveFieldQueryDTO;
import com.kmxd.ams.client.archive.vo.ArchiveFieldListVO;
import com.kmxd.ams.client.arrange.IBoxSvc;
import com.kmxd.ams.client.arrange.IDocumentSvc;
import com.kmxd.ams.client.arrange.IProjectSvc;
import com.kmxd.ams.client.arrange.IVolumeSvc;
import com.kmxd.ams.core.common.constant.ArchConst;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.enums.ArchStatusEnum;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.DynamicTableNameUtil;
import com.kmxd.ams.infra.arrange.base.SystemBO;
import com.kmxd.ams.infra.tmpl.dto.ArchTmplBO;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * 数据重复校验
 *
 * @author nickbi
 */
@AllArgsConstructor
@Component
public class DataRepeatCmd {
  private final IDocumentSvc documentSvc;
  private final IVolumeSvc volumeSvc;
  private final IBoxSvc boxSvc;
  private final IProjectSvc projectSvc;
  private final IArchiveFieldSvc archiveFieldSvc;

  public void checkRepeat(
      ArchStatusEnum statusEnum, Long archTypeId, String tableId, List<? extends SystemBO> list) {

    if (tableId.contains(ArchTmplBO.DOCUMENT)) {
      check(archTypeId, tableId, statusEnum, list, documentSvc);
    }
    if (tableId.contains(ArchTmplBO.VOLUME)) {
      check(archTypeId, tableId, statusEnum, list, volumeSvc);
    }
    if (tableId.contains(ArchTmplBO.BOX)) {
      check(archTypeId, tableId, statusEnum, list, boxSvc);
    }
    if (tableId.contains(ArchTmplBO.PROJECT)) {
      check(archTypeId, tableId, statusEnum, list, projectSvc);
    }
  }

  private void check(
      Long archTypeId,
      String tableId,
      ArchStatusEnum statusEnum,
      List<? extends SystemBO> list,
      IService service) {
    // 数据为空，直接返回
    if (ObjectUtil.isEmpty(list)) {
      return;
    }
    // 只处理待整理，已归档状态档案
    if (!ImmutableSet.of(ArchStatusEnum.S0, ArchStatusEnum.S1).contains(statusEnum)) {
      return;
    }
    // 重复校验字段查询
    ArchiveFieldQueryDTO qry = new ArchiveFieldQueryDTO();
    qry.setArchTypeId(archTypeId);
    qry.setTableId(tableId);
    if (ArchStatusEnum.S0.equals(statusEnum)) {
      qry.setAttrRepeatWarn(true);
    } else {
      qry.setAttrRepeatWarnArrange(true);
    }
    List<ArchiveFieldListVO> fieldListVOS = archiveFieldSvc.selectList(qry);
    if (ObjectUtil.isEmpty(fieldListVOS)) {
      return;
    }
    // 数据分区处理，避免数据过大内存溢出，此处可优化为多线程分批校验，需要实际基础设施决定
    List<? extends List<? extends SystemBO>> partition =
        Lists.partition(list, Constants.DEF_BATCH_PAGE_SIZE);
    // 按校验重复字段，分段校验
    fieldListVOS.forEach(
        field -> {
          String camel = StringUtils.underlineToCamel(field.getDbFieldKey());
          partition.forEach(
              // 分批处理
              part -> {
                // 得到当前批的校验值
                List<Object> values =
                    part.stream()
                        .map(
                            item -> {
                              JSONObject parseObj = JSONUtil.parseObj(item);
                              return parseObj.get(camel);
                            })
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());
                // 查询重复数据
                QueryWrapper<? extends SystemBO> query = Wrappers.query();
                query.eq(ArchConst.DEF_STATUS, statusEnum.getCode());
                query.in(field.getDbFieldKey(), values);
                DynamicTableNameUtil.setTableName(tableId);
                List<? extends SystemBO> docList = service.list(query);
                // 重复数据为空，进行下一批数据校验
                if (ObjectUtil.isEmpty(docList)) {
                  return;
                }
                // 获取重复数据，并拼接提示前端
                String repeatValueStr =
                    docList.stream()
                        .map(
                            doc -> {
                              JSONObject parseObj = JSONUtil.parseObj(doc);
                              return String.valueOf(
                                  Optional.ofNullable(parseObj.get(camel)).orElse(""));
                            })
                        .distinct()
                        .collect(Collectors.joining(","));
                // 提示重复数据信息
                throw BizTipException.instance(
                    ErrorCode.VALIDATE_DATA_REPEAT,
                    field.getDbFieldName() + ":" + repeatValueStr + "重复");
              });
        });
  }
}
